//*************************************************************************
//      
//      Code by Michael Carey
//      for Dr. Ostermann's CS444 Class
//      Ohio University - Spring 2007
//
//      Email:		mc12800@ohio.edu
//	Program: 	Adena Webserver      
//	Filename:	wserver.cc
//
//*************************************************************************
#include <iostream>
#include <arpa/inet.h>
#include <sys/types.h>
#include <signal.h>
#include <vector>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "wserver.h"
#include <fstream>

using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::istream;
using std::ifstream;
using std::fstream;
using std::getline;
using std::ios;

void tokenize(const string& str, vector<string>& tokens, const string& delimiters = " ");
string strip(string input);
string strip_header(string input);
static void reaper(int nothing);

#define MAX_CMD_LINE 100
#define MAX_PAGE_SIZE 500000

static int alive = 0;

//*************************************************************************
//
//	Default constructor. Doesn't do much as of right now besides
//	set our default to not display debug information.
//
//*************************************************************************
Wserver::Wserver(){
	usr_check = true;
	debug = false;	/* Default option for debugging, off */
}

//*************************************************************************
//
//	A lot of the code here was from Dr. Ostermann's online examples 
//	but has been modified to take either a port number or not and
//	also to only use tcp. Another modification was to store the master
//	socket to be passed to other functions and setup a debug.
//
//*************************************************************************
void Wserver::openSocket(int portnum){
	struct protoent *ppe;   /* pointer to protocol information entry*/
	struct sockaddr_in sin; /* an Internet endpoint address         */
	int s, type;		/* socket descriptor and socket type    */

	bzero((char *)&sin, sizeof(sin));	/* Zero out the sin struct */
	sin.sin_family = AF_INET;		/* Address Family */
	sin.sin_addr.s_addr = INADDR_ANY;	/* Use any available address */
	sin.sin_port = portnum;			/* Port number to use */
	
	// Map protocol name to protocol number (modified to only use tcp)
	if ( (ppe = getprotobyname("tcp")) == 0){
		fprintf(stderr,"can't get \"%s\" protocol entry\n", "tcp");
		exit(10);
	}

	// Type SOCK_STREAM or DGRAM, we use SOCK_STREAM
	type = SOCK_STREAM;

	// Allocate a socket
	s = socket(PF_INET, type, ppe->p_proto);
	if (s < 0) {
		perror("Socket Allocation Error");
		exit(12);
	}

	// Check to see if we want a random socket or not
	if(portnum != 0){
		if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		    perror("Socket Binding Error");
		    exit(11);
		}
	}

	// Listen on 5 connections concurrently
	if (type == SOCK_STREAM && listen(s, 5) < 0) {
		perror("Port Listening Error");
		exit(18);
	}
	
	// Store our master socket in the class
	m_socket = s;
}

//*************************************************************************
//
//	Get information about the server that Adena is running on 
//	and print it to the screen. In this case we output the hostname
//	and the port which adena is running on. For added fun, we also
//	output the short path and the full path.
//
//*************************************************************************
void Wserver::servDetails(){
	int port = 0;
	char hostname[80];
	struct sockaddr_in sin;
	int sinlen = sizeof(sin);

	// Fill the sockaddr_in struct with information
	if (getsockname(m_socket, (struct sockaddr *)&sin, (socklen_t *)&sinlen) != 0) {
		perror("Error Getting Socket Details");
		exit(-1);
	}

	// Get the port number we are running on
	port = ntohs(sin.sin_port);

	// Get the hostname we are running on
	gethostname(hostname, 80);

	// Output the information we got
	cout << "Server starting, to see my demo enter:" << endl;
	cout << "Local: " << endl;
	cout << "http://" << hostname << ":" << port << "/\n";
	cout << "Remote: " << endl;
	cout << "http://" << hostname << ".cs.ohiou.edu:" << port << "/\n";
}

//*************************************************************************
//
//	Starts the server accepting connections and keeps doing it
//	until the server is killed. Uses the master socket from 
//	our default constructor. 
//
//	Closes each socket after it services the requests.
//
//*************************************************************************
int Wserver::start(){
	struct sockaddr_in fsin;
	int alen = 0;
	pid_t pid;
	sigset_t *blocker = new sigset_t, *normal = new sigset_t;

	sigemptyset(blocker);
	sigemptyset(normal);
	sigaddset(blocker, SIGCHLD);

	// Keep accepting connections until the server is killed
	while(1){
		// Create a new slave socket
			
		sigprocmask(SIG_BLOCK, blocker, normal); 
		s_socket = accept(m_socket, (struct sockaddr *)&fsin, (socklen_t *)&alen);		
		sigprocmask(SIG_BLOCK, normal, blocker); 
	
		if(s_socket < 0){
			perror("Accept Failed");
			exit(21);
		}
		
		// Create a new process to service each request
		signal(SIGCHLD, reaper);	

		pid = fork();
		if(pid == 0){
			close(m_socket);
			// Service the request
			request(s_socket);
			exit(0);

		} else if (pid < 0){
			close(s_socket);
			perror("Fork Failed");
			exit(1);
		}

		alive++;
		// Close the slave socket
		close(s_socket);
	}

}

void Wserver::rate_send(int fd, string data, string filename){
	char buffer[MAX_CMD_LINE];
	FILE *redir_file;		
	int speed = 0, pieces = 0; 
	string seg_send;

	getcwd(buffer, MAX_CMD_LINE);
	strcat(buffer, filename.c_str()); 	/* Requested Page */
	strcat(buffer, ".ratelimit");	 	/* Requested Page */

	// Check to make sure we can read the file
	if( access(buffer, R_OK) != 0){
		send(fd, data.c_str(), data.size(), 0);
	} else {
		redir_file = fopen(buffer, "r");
		fscanf(redir_file, "%d", &speed);
		
		if(speed < 12){
			pieces = speed;
		} else if (speed > (int)data.size()){
			speed = (int)data.size();
		} else {
			pieces = speed / 12;
		}

		for(int i = 0; i <= (int)data.size() / pieces; i++){
			if((i * pieces) + pieces < (int)data.size()){
				if(i == 0){
					seg_send = data.substr((i * pieces), pieces); 
					if(debug) cout << "Sending " << i*pieces << " - " << i*pieces + pieces << endl;
				} else {
					seg_send = data.substr((i * pieces) + 1, pieces); 
					if(debug) cout << "Sending " << i*pieces+1 << " - " << i*pieces + pieces << endl;
				}	
				send(fd, seg_send.c_str(), pieces, 0);
				usleep(1000);
				
			} else {
				int stop_pos = data.size() - ((i * pieces) + 1);
				seg_send = data.substr((i * pieces) + 1, stop_pos + 1); 
				if(debug) cout << "Sending " << i*pieces+1 << " - " << i*pieces + stop_pos << endl;
				send(fd, seg_send.c_str(), pieces, 0);
				usleep(1000);
			}
		}

		fclose(redir_file);
	}	

}

void Wserver::rate_send(int fd, char data[], string filename, int size){
	char buffer[MAX_CMD_LINE];
	FILE *redir_file;		
	int speed = 0, pieces = 0; 

	getcwd(buffer, MAX_CMD_LINE);
	strcat(buffer, filename.c_str()); 	/* Requested Page */
	strcat(buffer, ".ratelimit");	 	/* Requested Page */

	// Check to make sure we can read the file
	if( access(buffer, R_OK) != 0){
		if(debug) cout << "No ratelimit file found\n";
		send(fd, data, size, 0);
	} else {
		if(debug) cout << "Rate limiting\n";
		redir_file = fopen(buffer, "r");
		fscanf(redir_file, "%d", &speed);
			
		if(speed < 12){
			pieces = speed;
		} else if (speed > size){
			speed = size;
		} else {
			pieces = speed / 12;
		}
						
		char seg_send[pieces];

		for(int i = 0; i <= size / pieces; i++){
			if((i * pieces) + pieces < size){
				if(i == 0){
					memcpy(seg_send, data + (i * pieces), pieces);
					if(debug) cout << "Sending " << i*pieces << " - " << i*pieces + pieces << endl;
				} else {
					memcpy(seg_send, data + (i * pieces) + 1, pieces);
					if(debug) cout << "Sending " << i*pieces+1 << " - " << i*pieces + pieces << endl;
				}	
				send(fd, seg_send, pieces, 0);
				usleep(1000);
			} else {
				int stop_pos = size - ((i * pieces));
				memcpy(seg_send, data + (i * pieces) + 1, stop_pos + 1);
				if(debug) cout << "Sending " << i*pieces+1 << " - " << i*pieces + stop_pos << endl;
				send(fd, seg_send, pieces, 0);
				usleep(1000);
			}
		}

		fclose(redir_file);
	}	

}

//*************************************************************************
//
//	Deal with the response here. Dealing with response includes first 
//	breaking up the header and finding out exactly what the request was.
//	
//	Then, we look to see if the security on the file will allow us
//	to fulfill the request. 
//
//	Then we need to construct the return header that tells the browser
//	what happened to it's request.
//
//	Finally, if the security passed, we attach the file at the end of
//	the header and send the whole shebang back to the socket.
//
//*************************************************************************
int Wserver::request(int skt){
	int fd = s_socket, status_code = 0, len = 0;
	char req_buf[400];
	string page = "", header;	
	vector<string> tokens;	/* Split string holder */
	FILE *infile = 0;	/* Input File */
	long isize = 0;		/* Input Size */
	char buffer[MAX_CMD_LINE];	/* Input Buffer */	
	string send_s = "";	/* Final Send String */
	bool redirect = false;  /* Should we redirect */ 

	// Clear out our storage space
	memset(buffer, 0, 100);

	// Read the request
	len = recv(fd, req_buf, sizeof(req_buf), 0);

	char recd[len];
	for(int i = 0; i < len; i++){
		recd[i] = req_buf[i];
	}

	if(debug){
		cout << "******************** REQUEST HEADER ********************\n";
		cout << recd;
		cout << "********************************************************\n";
	}

	// Grab the name of the page the client requested
	page = parseRequest(recd, sizeof(recd));	
		
	// If it's a request we don't know how to handle, don't
	if(page == "ERR") return(99);

	// Check for a redirect file
	redirect = findRedir(page);

	if(redirect){
		if(debug) cout << "Beginning Redirecting\n";
		doRedir(page);	// do all of the redirect stuff
	} else {
		// Check to see if we have permissions to acces the file
		status_code = checkFile(page);
		
		if(debug) cout << "Generated Response Code: " << status_code << endl;

		// Get the current working directory and add the filename
		// Security measure here, only allows current directory
		getcwd(buffer, MAX_CMD_LINE);

		// Append a page name to the current directory
		if(status_code == 200){
			strcat(buffer, page.c_str()); 	/* Requested Page */
		} else if (status_code == 404){
			strcat(buffer, "/404.html");	/* Error Page */
		} else if(status_code == 403){
			strcat(buffer, "/403.html");	/* Access Forbidden */
		}
	
		ifstream is;
		is.open(buffer, ios::binary);
		
		is.seekg(0, ios::end);
		isize = is.tellg();
		is.seekg(0, ios::beg);

		char * data = new char[isize];

		is.read(data, isize);
		is.close();

		// Build the response header based upon the code
		header = responseHeader(status_code, page, isize);
	
		if(debug){
			cout << "******************** RESPONSE HEADER ********************\n";
			cout << header << endl;
			cout << "*********************************************************\n";
		}
	
		// Generate the full data (header + page)
		char snd[isize + header.size()];
	
		if(page.find(".png") != string::npos || page.find(".gif") != string::npos){
			memcpy(snd, header.c_str(), header.size() - 2);
 			memcpy(snd + header.size() - 2, data, isize);	
			rate_send(fd, snd, page, header.size() + isize - 2);
		} else {
			memcpy(snd, header.c_str(), header.size() - 2);
 			memcpy(snd + header.size() - 2, data, isize);	
			rate_send(fd, snd, page, header.size() + isize - 2);
		}	

		if(debug){ 
			cout << "Sending with size: " << isize;
			cout << " (header size of " << header.size() << ")\n\n";
		}
			
		// Close the read in file
		fclose(infile);
	}
	return 0;
}

//*************************************************************************
//
//	When the request comes in, it comes in as a character array.
//	We look at the character array and try and find the GET / HTTP/X.X.
//	
//	This will tell us the file we are looking for and the method we are
//	going to use. For this project we will only parse GET requests so
//	we exit otherwise. 
//
//	Since we really don't care about the rest (as far as I know we
//	aren't dealing with caching or anything) just return the string
//	that is the filename.
//
//***************************************************************************
string Wserver::parseRequest(char *input, size_t size){
	int newline = 0;
	string req_string;	
	vector<string> tokens;	
	
	// Find the first newline, we only want the first line of the header.
	for(int i = 1; i < (int)size; i++){
		if(input[i] == '\n'){
			newline = i;
			break;	/* Messy way to do it, but it works */
		}
	}

	// Copy the first line into a new string
	for(int i = 0; i < newline; i++) req_string += input[i];

	// Split the first line into three parts, the METHOD,
	// the PAGE requested, and the HTTP TYPE
	tokenize(req_string, tokens);
	
	// Make sure it's GET type, we won't handle the rest
	if(tokens[0] != "GET"){
		cout << "Method we don't know how to handle\n";
		return "ERR";
	} 

	// Check to see if the request didn't have a page,
	// if it didn't go to the index.
	if(tokens[1] == "/"){
		return "/index.html";
	} else {
		return tokens[1];
	}
}

//*******************************************************************************
//
//	Check file performs certain checks to make sure that the file requested
//	is actaully supposed to be displayed. The first check is to make sure 
//	the file exists. If it doesn't we will return HTTP/1.1 status code 404.
//
//	Our second check looks to see if the user id of the file matches our
//	userid. For our purposes, this allows us to only serve files that belong
//	to us. Normally, we'd check the group here instead of the userid but
//	as this project is one person only.
//
//	We also strip the incoming string of anything but 0-9, a-Z, ., /, and
//	-. This will prevent people from trying to use special characters
//	to acces files they shouldn't be allowed to access. This also prevents
//	users from entering ../../ which would normally bring them back a few
//	directories. While most browsers strip this we still must double check
//	and test anyways.
//
//	Proper return values are 200, 403, and 404. 
//	200 - OK
//	403 - FORBIDDEN
//	404 - NOT FOUND
//
//*******************************************************************************

int Wserver::checkFile(string filename){
	struct stat file_stat;
	string cleaned = strip(filename);
	char buffer[MAX_CMD_LINE];
	
	getcwd(buffer, MAX_CMD_LINE);
	strcat(buffer, cleaned.c_str());
	

	// Start more advanced file checks
	if( stat(buffer, &file_stat) == 0){
		if(usr_check){
			// See if the file belongs to us
			if( file_stat.st_uid != getuid() ){
				return 403;
			}
		}
	} else {
		// File Doesn't Exist
		return 404;
	}


	// Final check to make sure we can read the file
	if( access(buffer, R_OK) != 0)
		return 403;

	// File exists and everything permissions wise is ok
	return 200;
}

//*******************************************************************************
//
//	Builds the response header based upon the HTTP/1.1 status code passed to
//	it. This is based upon the google HTTP/1.1 status header and contains
//	most of the same fields.
//
//*******************************************************************************
string Wserver::responseHeader(int code, string path, int size){
	string header = "", s_word, t_word;
	char conversion[20], str_time[80];
	time_t rawtime;
	struct tm * timeinfo;
	vector<string> con_type;

	time ( &rawtime );
	timeinfo = localtime ( &rawtime );

	strftime(str_time, 80, "%a, %d %b %Y %X %Z\r\n",timeinfo);

	if(code == 200){
		s_word = "OK";
	} else if (code == 403){
		s_word = "FORBIDDEN";
	} else if (code == 404){
		s_word = "NOT FOUND";
	} else {
		s_word = "BAD REQUEST";
	}
	
	tokenize(path, con_type, ".");
	
	if(con_type[con_type.size() - 1] == "jpg" || con_type[con_type.size() - 1] == "jpeg"){
		t_word = "image/jpg";
	} else if (con_type[con_type.size() - 1] == "gif"){
		t_word = "image/gif";
	} else if (con_type[con_type.size() - 1] == "png"){
		t_word = "image/png";
	} else if (con_type[con_type.size() - 1] == "mov"){
		t_word = "video/quicktime";
	} else if (con_type[con_type.size() - 1] == "avi"){
		t_word = "video/x-msvideo";
	} else if (con_type[con_type.size() - 1] == "pdf"){
		t_word = "application/pdf";
	} else if (con_type[con_type.size() - 1] == "iso"){
		t_word = "application/octet-stream";
	} else if (con_type[con_type.size() - 1] == "css"){
		t_word = "text/css";
	} else if (con_type[con_type.size() - 1] == "txt"){
		t_word = "text/plain";
	} else {
		t_word = "text/html";
	} 

	sprintf(conversion, "%d", code); 
	header = "HTTP/1.1 " + string(conversion) + " " + s_word + "\r\n";
	header += "Content-Type: " + t_word + "\r\n";	
	sprintf(conversion, "%d", size);
	header += "Content-Length: " + string(conversion) + "\r\n";
	header += "Server: Adena/0.2\r\n";
	header += "Date: " + string(str_time) + "\r\n\r\n"; 

	return header;
}	

//*********************************************************************************
//
//	Check to see if there is a .redirect file based upon the filename and 
//
//	the full path. For safety, we still strip bad characters.
//
//*********************************************************************************
bool Wserver::findRedir(string r_file){
	string n_file;
	struct stat file_stat;
	char buffer[MAX_CMD_LINE];	
	
	n_file = strip(r_file) + ".redirect";
	getcwd(buffer, MAX_CMD_LINE);
	strcat(buffer, n_file.c_str());

	if( stat(buffer, &file_stat) == 0){
		return true;
	} 

	return false;
}

//*********************************************************************************
//
//	If we are asked to do a redirect, first find out where we need to redirect
//	to. Then check to see if there are any other commands that we need to do 
//	to the file. Do those, and finally return the file to the user.
//
//*********************************************************************************
void Wserver::doRedir(string i_file){
	string n_file;
	char buffer[MAX_CMD_LINE], url[100]; 
	FILE *infile;
	int fd = 0; //, len = 0;
	vector<string> strip_url;

	n_file = strip(i_file) + ".redirect"; 	/* strip the input file */
	getcwd(buffer, MAX_CMD_LINE);		/* get the current directory */
	strcat(buffer, n_file.c_str());		/* append current dir on in file */
	
	infile = fopen(buffer, "rb"); 		/* Open up the redirect file */
	fscanf(infile, "%s", url);		/* Read the first line of the file */
	string s_url(url);			/* Convert the url to string */
	tokenize(s_url, strip_url, "/");	/* Split the url into stings via '/' */

	// Get ip address from the url
	hostent *record = gethostbyname(strip_url[1].c_str());
	if (record == NULL){ 
		perror("Getting host by name failed");
    		exit(1);
	}
  
	// Convert the hostent into a in_addr for later
	in_addr * address=(in_addr * )record->h_addr; 
	fd = getWebSocket(address); /* Create a connection to the address we found */

	// Build the GET string
	string request = "GET /";
	for(int i = 2; i < (int)strip_url.size(); i++){
		request += strip_url[i];
		if(i != (int)strip_url.size() - 1)
			request += "/";
	} 	
	request += " HTTP/1.0\r\n\r\n";

	getWebpage(request, fd, buffer, i_file);

	// Close the file stream
	fclose(infile);
}

//*********************************************************************************
//
//	Get a new socket for the address of the site that we want
//
//*********************************************************************************
int Wserver::getWebSocket(in_addr *address){
	struct sockaddr_in peer;
	bzero((char *)&peer, sizeof(peer));
	struct protoent *ppe;   /* pointer to protocol information entry*/
	int s = 0; //, len = 0;	

	peer.sin_family = AF_INET;
	peer.sin_port = htons(80);
	peer.sin_addr = *address;
	
	// Map protocol name to protocol number (modified to only use tcp)
	if ( (ppe = getprotobyname("tcp")) == 0){
		fprintf(stderr,"can't get \"%s\" protocol entry\n", "tcp");
		exit(10);
	}

	s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
	if (s < 0) {
		perror("Socket Allocation Error");
		exit(12);
	}

	if( connect(s, (struct sockaddr*)&peer, sizeof(peer)) != 0){
		cout << "Error Connecting";
	}

	return s;
}

//*********************************************************************************
//
//	Get the redirected webpage, run shell script commands on it, and 
//	put it on the send buffer.
//
//*********************************************************************************
int Wserver::getWebpage(string request, int fd, char *buffer, string filename){
	int len = 0, sentinel = 0;
	char rbuf[MAX_PAGE_SIZE], tmp_file[50];
	char line[100000];
	FILE *temp, *infile, *f;
	vector<string> commands;
	string to_screen, prep_send;
	char *pch;

        infile = fopen(buffer, "rb");          /* Open up the redirect file */
	
	// Get every line of the .redirect file. We'll discard the first one later
	sentinel  = getc(infile);
	while(!feof(infile)){	
		ungetc(sentinel, infile);
		fgets(line, sizeof(line), infile);
		commands.push_back(strip(string(line)));		
		sentinel = getc(infile);		
	}

	// Send out our request string
	if( send(fd, request.c_str(), request.size(), 0) < 1){
		perror("Sending error");
		exit(1);
	}

	// Block until all data has been recieved
	len = recv(fd, rbuf, sizeof(rbuf), MSG_WAITALL);
	char recd[len];

	// Copy the good data to a new buffer
	for(int i = 0; i < len; i++) recd[i] = rbuf[i];

	// If it's not just a url in the file, do the commands
	if(commands.size() > 1){
		tmpnam(tmp_file);	/* Create a temporary file */
		temp = fopen(tmp_file, "wb+");
		fwrite(recd, sizeof(recd[0]), sizeof(recd), temp);  
		fclose(temp);
	
		temp = fopen(tmp_file, "r+");

		/* For each command modify and rewind the file */
		for(int i = 1; i < (int)commands.size(); i++){
			string modifier = commands[i] + " < " + string(tmp_file);

			// Open the command as a file stream
			f = popen(modifier.c_str(), "r+");
			if(f == NULL) perror("Popen Failed");
	
			// Read every line removing the newlines
			while ((fgets(line,sizeof(line),f)) != NULL) {
				if ((pch = strchr(line,'\n')) != NULL) 
					*pch = '\00';  // nuke the newline
				fputs(line, temp);
			}

			modifier = "";
			pclose(f);
			rewind(temp);
		}
			
		rewind(temp);

		// Read the entire file into the string		
		sentinel = getc(temp);
		while(!feof(temp)){	
			ungetc(sentinel, temp);
			fgets(line, sizeof(line), temp);
			prep_send += string(line);
			sentinel = getc(temp);		
		}
		fclose(temp);		

		// Remove the headers
		prep_send = strip_header(prep_send);

		string new_header = responseHeader(200, filename, prep_send.size());
		rate_send(s_socket, new_header + prep_send, filename);

		// Remove the temp file
		if( remove(tmp_file) != 0 ) perror( "Error deleting file" );
	} else {
		// If there is only one, just send the file back out
		rate_send(s_socket, string(recd), filename);
	}

	return 0;
}

//*********************************************************************************
//
// 		Example Tokenizing code from: 
//
// 	http://www.oopweb.com/CPP/Documents/
//	CPPHOWTO/Volume/C++Programming-HOWTO-7.html
//
//	Takes a string as an input, a vector to return to, and a set of delimiters
//	to split with. Searches through the string looking for the delimiters and
//	when it finds one, splits the string at the point and inserts it into the
//	vector.
//
//	Delimeters default value is " ", a.k.a. split at spaces. This can be
//	set to any valid character.
//
//*********************************************************************************
void tokenize(const string& str, vector<string>& tokens, const string& delimiters){
	// Skip delimiters at beginning.
	string::size_type lastPos = str.find_first_not_of(delimiters, 0);

	// Find first "non-delimiter".
	string::size_type pos = str.find_first_of(delimiters, lastPos);
	
	 while (string::npos != pos || string::npos != lastPos){
		// Found a token, add it to the vector.
		tokens.push_back(str.substr(lastPos, pos - lastPos));

		// Skip delimiters.  Note the "not_of"
		lastPos = str.find_first_not_of(delimiters, pos);

		// Find next "non-delimiter"
		pos = str.find_first_of(delimiters, lastPos);
	}
}

//**********************************************************************************
//
//	Strip the input string of everything except a-z, A-Z, 1-9, -, and _.
//
//**********************************************************************************
string strip(string input){
	string ret_string;
	string::iterator p;

	// Clear out any attempts to go back a directory
	while(input.find("../") != string::npos){
		input.erase(input.find("../"), 3);
	}

	// Create a new string without all the other stuff in it	
	for(int i = 0; i < (int)input.size(); i++){
		if( (input.at(i) > 43 && input.at(i) < 58) || (input.at(i) > 64 && input.at(i) < 123) 
			|| input.at(i) == 95 || input.at(i) == 32 || input.at(i) == 124 || input.at(i) == 39){
		 	ret_string += input.at(i);
		}
	}

	return ret_string;
}

//**********************************************************************************
//
//	Strip the HTTP Headers off the incoming string
//
//**********************************************************************************
string strip_header(string input){
	u_int loc1, loc2;

	loc1 = input.find("<HTML>");
	loc2 = input.find("<html>");

	// Make sure we really have <html> or <HTML> in the file
	if(loc1 == string::npos && loc2	== string::npos) return input;
	
	// Choose the nearest one
	if(loc1 < loc2 && loc1 != string::npos){
		return input.substr(loc1, input.size() - loc1);
	} else {
		return input.substr(loc2, input.size() - loc2);
	}

	return input; // If nothing else, at least return as is
}

//**********************************************************************************
//
//	Kill any zombie processes left if a fork() exits without killing it for
//	one reason or another
//
//**********************************************************************************
static void reaper(int nothing){
    int pid;
    int status;

    printf("Reaper Called:\n");
	
    while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0) {
	printf("  reaper: pid is %d\n", pid);
	--alive;  // Caveat Emptor - race condition here!
    }
}

